#include <net/if.h>
#include <sys/ioctl.h>
#include <cstdlib>
#include <cerrno>
#include <cstdio>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <iostream>
#include <sys/poll.h>
#include <sys/epoll.h>
#include <set>
#include <map>

#include "signal.h"
#include "CCycleBuffer.h"
#include "unp.h"
#include "network.h"

using namespace std;

static const int MAX_COORDINATOR = 20;

struct tcpConnectInfo
{
	int socket;
	CCycleBuffer *buffer;
};

int main(int argc, char** argv)
{
	set<int> idSet;
	map<int, CCycleBuffer*> sock2buff;

	tcpConnectInfo tcpConnectInfos[MAX_COORDINATOR];
	for(int i=0;i<MAX_COORDINATOR;++i)
	{
		tcpConnectInfos[i].socket = -1;
		tcpConnectInfos[i].buffer = new CCycleBuffer(2000);
	}

	/*
	 * Set up TCP server for receive cmd from gateway
	 */
	tcpServer server;
	server.Listen(50000);

	cout<<"Gateway: Listen on port "<<50000<<endl;

 	int nfds;
	const int MAX_EVENTS = 30;
	struct epoll_event ev, events[MAX_EVENTS];
	int epollfd = epoll_create(MAX_EVENTS);
	if(epollfd == -1)
	{
		perror("epoll_create");
		exit(-1);
	}

	//ADD control for tcp server
	ev.events = EPOLLIN;
	ev.data.fd = server.getSocket();
	if(epoll_ctl(epollfd, EPOLL_CTL_ADD, ev.data.fd, &ev) == -1)
	{
		perror("epoll_ctl: netMS.getsocket()");
		exit(-1);
	}

	ev.events = EPOLLIN;
	ev.data.fd = 0;
	if(epoll_ctl(epollfd, EPOLL_CTL_ADD, ev.data.fd, &ev) == -1)
	{
		perror("epoll_ctl: netMS.getsocket()");
		exit(-1);
	}

	struct sockaddr_in cliaddr;
	socklen_t clilen = sizeof(cliaddr);

	int res;
	char buff[65535];

	char resetCMD[] = {'\xAA','\x44','\x01','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\xFF','\x01','\x00','\x18','\x20','\x01','\x00','\x01','\x00','\x00'};
	char paddr[INET_ADDRSTRLEN];
	int count = 0;
  
	time_t start, now;
	//struct tm *timenow=NULL;

	while(true)
	{
		nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
		for(int i = 0;i<nfds;++i)
		{
			if(events[i].data.fd == server.getSocket())	//connect from node
			{
				int connfd = TEMP_FAILURE_RETRY(accept(events[i].data.fd, (struct sockaddr *)&cliaddr, &clilen));

				printf("--------------------------connfd:%d\n", connfd);
				cout<<"Node: Receive connect from: "
				    <<inet_ntop(AF_INET, &cliaddr.sin_addr, paddr, INET_ADDRSTRLEN)
						<<":"
						<<ntohs(cliaddr.sin_port)
						<<endl;
				ev.events = EPOLLIN;
				ev.data.fd = connfd;
				epoll_ctl(epollfd, EPOLL_CTL_ADD, ev.data.fd, &ev);

				int i=0;
				for(;i<MAX_COORDINATOR;++i)
					if(tcpConnectInfos[i].socket == -1)
						break;
	
				if(i<MAX_COORDINATOR)
				{
					tcpConnectInfos[i].buffer->Empty();
					tcpConnectInfos[i].socket = connfd;
					sock2buff[connfd] = tcpConnectInfos[i].buffer;
				}
			}
			else if(events[i].data.fd == 0)
			{
				read(0,buff, sizeof(buff));
				/*
				for(int i=0;i<res;i++)
					printf("%02hhx ", buff[i]);
				cout<<endl;
				cout<<"Read from stdin"<<endl;
				cout<<buff<<endl;
				*/
				if(memcmp(buff, "reset", 5) == 0)
				{
					idSet.clear();
					count = 0;

					for(int i=0;i<MAX_COORDINATOR;++i)
					{
						if(tcpConnectInfos[i].socket > 0)
						{
							res = TEMP_FAILURE_RETRY(send(tcpConnectInfos[i].socket, resetCMD, sizeof(resetCMD), 0));
							if(res < 0)
							{
								close(tcpConnectInfos[i].socket);
								tcpConnectInfos[i].socket = -1;
							}
						}
					}
				}
			}
			else	//data from node
			{
				res = recv(events[i].data.fd, buff, sizeof(buff), 0);
				if(res > 0)
				{
					sock2buff[events[i].data.fd]->Write(buff, res);
					char header, subNet;
					int nodeId;
					unsigned char nodeAddr[3];
					while(sock2buff[events[i].data.fd]->GetLength() >= 4)
					{
						sock2buff[events[i].data.fd]->Read(&header, 1);
						if(header != '\xAA')
							continue;
					
						sock2buff[events[i].data.fd]->Read((char *)nodeAddr, 3);
						subNet = nodeAddr[0];
						nodeId = nodeAddr[1]*256 + nodeAddr[2];
						//cout<<(int)subNet<<" : "<<nodeId<<endl;
						if(idSet.count(256*256*subNet + nodeId) == 0)
						{
							cout<<"++++++++++"<<endl;
							idSet.insert(256*256*subNet + nodeId);
							//cout<<"Node :"<<(int)subNet<<" "<<nodeId<<endl;
							printf("Node: subNet %02hhx netID %02hhx %02hhx\n", nodeAddr[0],  nodeAddr[1] , nodeAddr[2]);
							count++;
							cout<<"Count: "<<count<<endl;
							if(count == 1)
							{
								time(&start);
								start += 3;
								//timestart= localtime(&start);
								//cout<<"Time: "<<asctime(timestart)<<endl;
							}
							else
							{
								//cout<<now<<endl;
								time(&now);
								int inter = now-start < 0 ? 0: now-start;
								cout<<"Time: "<<inter<<"s"<<endl;
								//timenow = localtime(&now);
								//cout<<timenow->tm_sec<<":"<<timestart->tm_sec<<endl;
								//cout<<60*(timenow->tm_min-timestart->tm_min)+timenow->tm_sec-timestart->tm_sec<<endl;
							}
						}
					}
				}
				else
				{
					if(res == 0)
						cout<<"close by node"<<endl;
					else
						perror("receive error:");

					int i = 0;
					for(;i<MAX_COORDINATOR;++i)
						if(tcpConnectInfos[i].socket == events[i].data.fd)
							break;

					if(i<MAX_COORDINATOR)
					{
						close(tcpConnectInfos[i].socket);
						tcpConnectInfos[i].socket = -1;
					}
				}
			}
		}
	}//end while(true)
	Close(server.getSocket()); //close listen fd

	return 0;
}//end main
